import { Component, OnInit, Input, Output, EventEmitter,
  ChangeDetectorRef, Inject} from '@angular/core';

import { Shape, SHAPE_NAME_SEQ, getShapeInfo } from '../models/shape';
import { Direction as Dir } from '../models/direction';
import { Rotation, rotate } from '../models/rotation';

import { MinoCmp } from './mino.cmp';
import { DIMENSION, Coord, Dimension } from '../models/dimension';

const ColorValue: { [key: string]: string } = {
  O: '#F9EC5C',
  I: '#53BAEE',
  T: '#E651E2',
  L: '#F3AC52',
  J: '#537DDE',
  S: '#63CD35',
  Z: '#EA3F1C',
}

export const rotateSeq = [Dir.Up, Dir.Right, Dir.Down, Dir.Left];


@Component({
  moduleId: module.id,
  selector: 'th-tetrimino',
  templateUrl: 'tetrimino.cmp.html',
  styleUrls: ['tetrimino.cmp.css'],
  directives: [MinoCmp]
})
export class TetriminoCmp {

  @Input() x: number = 0;
  @Input() y: number = 0;
  @Output() touchDown = new EventEmitter();

  isShadow: boolean;
  shadow: TetriminoCmp;

  private _minosPos: Coord[];
  private _shape: Shape = null;
  @Input()
  set shape(shp: Shape) { this._minosPos = getShapeInfo(this._shape = shp); }
  get shape() { return this._shape; }
  get color() { return ColorValue[this.shape]; }

  reset(shp: Shape, matrix: any[][]) {
    this.shape = shp;
    this.x = matrix[0].length / 2 - ('OI'.indexOf(shp) >= 0 ? 0 : 1), this.y = 1;
    return !_gonnaCollide(this._minosPos, matrix, this.x, this.y);
  }

  getMinoProps(idx: number) {
    let coord = this._minosPos[idx];
    return {
      coord: <Coord>[this.x + coord[0], this.y + coord[1]],
      color: this.color
    };
  }

  getOutline() {
    return {
      color: this.color,
      coords: this._minosPos.map(m => <Coord>[m[0] + this.x, m[1] + this.y])
    };
  }

  ///Couple it with a ghost piece as its shadow
  addShadow(shadow: TetriminoCmp, matrix: any[][]) {
    shadow.isShadow = true;
    this.shadow = shadow;
    // this._updateShadow(matrix);
    return this;
  }

  private _updateShadow(matrix?: any[][]) {
    if (matrix && this.shadow) {
      let shw = this.shadow;
      shw.shape = this.shape;
      shw._minosPos = this._minosPos;
      shw.x = this.x;
      shw.y = this.y;
      shw.hardDrop(matrix);
      if (shw.y === this.y) { this.touchDown.emit('Touch'); }
    }
    return this;
  }

  moveTo(coord: Coord, matrix?: any[][]) {
    this.x = coord[0], this.y = coord[1];    
    return this._updateShadow(matrix);
  }

  private _setMinosPos(posNew: Coord[], matrix?: any[][]) {
    this._minosPos = posNew;
    this._updateShadow(matrix);
  }

  tryMove(dir: Dir, matrix: any[][]) {
    let yNext = this.y + (dir === Dir.Down ? 1 : 0)
      , xNext = this.x + (dir === Dir.Left ? -1 : dir === Dir.Right ? 1 : 0)
      ;
    return _gonnaCollide(this._minosPos, matrix, xNext, yNext) ? null :
      this.moveTo([xNext, yNext], matrix);
  }

  hardDrop(matrix: any[][]) {
    let x = this.x
      , y = this.y
      , mps = this._minosPos
      ;
    while (!_gonnaCollide(mps, matrix, x, y + 1)) { y++; }
    this.y = y;
  }

  tryRotate(dir: Rotation, matrix: any[][]) {
    let posNew =
      rotate(this._minosPos, dir, 'OI'.indexOf(this.shape) >= 0 ? -0.5 : 0);
    return _gonnaCollide(posNew, matrix, this.x, this.y) ? null :
      this._setMinosPos(posNew, matrix);
  }

}



function _gonnaCollide(minos: Coord[], matrix: any[][], x: number, y: number) {
  let xMax = matrix[0].length
    , yMax = matrix.length
    ;
  return minos.some(m => {
    let mx = m[0] + x, my = m[1] + y;
    return !(0 <= mx && mx < xMax && (my < 0 || my < yMax && !matrix[my][mx]));
  });
}